#include "clerk.h"
#include "raftServerRpcUtil.h"
#include "util.h"
#include <string>
#include <vector>

Clerk::Clerk() : m_clientId(Uuid()), m_requestId(0), m_recentLeaderId(0) {}

// 初始化客户端：从配置文件加载集群节点信息，建立与所有节点的RPC连接
// 参数：configFileName - 存储集群节点IP和端口的配置文件路径
void Clerk::Init(std::string configFileName) {
    // 1. 加载配置文件（使用自定义RPC框架的配置工具MprpcConfig）
    rpc::MprpcConfig config;
    config.LoadConfig(configFileName.c_str());  // 解析配置文件内容
  
    // 2. 读取所有节点的IP和端口（配置文件中以"node0ip"、"node0port"、"node1ip"等形式存储）
    std::vector<std::pair<std::string, short>> ipPortVt;  // 存储节点的<IP, 端口>对
    for (int i = 0; i < INT_MAX - 1; ++i) {  // 循环读取直到没有更多节点
        std::string node = "node" + std::to_string(i);      // 节点标识（如"node0"、"node1"）
        std::string nodeIp = config.Load(node + "ip");      // 读取该节点的IP
        std::string nodePortStr = config.Load(node + "port");  // 读取该节点的端口
        if (nodeIp.empty()) {  // 若IP为空，说明已读完所有节点，退出循环
            break;
        }
        // 将IP和端口（字符串转short）存入向量
        ipPortVt.emplace_back(nodeIp, atoi(nodePortStr.c_str()));
    }
  
    // 3. 为每个节点创建RPC连接工具（raftServerRpcUtil），并保存到m_servers中
    for (const auto& item : ipPortVt) {
        std::string ip = item.first;
        short port = item.second;
        // 创建raftServerRpcUtil实例（管理与该节点的RPC通信）
        auto* rpc = new raftServerRpcUtil(ip, port);
        // 用智能指针管理RPC实例（自动释放内存，避免泄漏）
        m_servers.push_back(std::shared_ptr<raftServerRpcUtil>(rpc));
    }
}


void Clerk::Put(std::string key, std::string value) { PutAppend(key, value, "Put"); }

void Clerk::Append(std::string key, std::string value) { PutAppend(key, value, "Append"); }
  

// 核心实现：向KV服务器发送Get请求（查询key对应的value）
// 返回值：key对应的value（若不存在返回空字符串）
std::string Clerk::Get(std::string key) {
    m_requestId++;  // 递增请求ID（确保每个请求唯一）
    auto requestId = m_requestId;  // 保存当前请求ID
    int server = m_recentLeaderId;  // 从最近已知的领导者节点开始尝试
  
    // 循环重试：直到找到真正的领导者并成功获取结果
    while (true) {
        // 构造Get请求参数
        raftKVRpcProctoc::GetArgs args;
        args.set_key(key);              // 设置要查询的key
        args.set_clientid(m_clientId);  // 客户端唯一标识
        args.set_requestid(requestId);  // 本次请求唯一标识
  
        raftKVRpcProctoc::GetReply reply;  // 用于接收服务器响应
        // 调用RPC工具向目标节点发送请求
        bool ok = m_servers[server]->Get(&args, &reply);
  
        // 若RPC调用失败或服务器返回"错误的领导者"，切换节点重试
        if (!ok || reply.err() == common::ErrWrongLeader) {
            server = (server + 1) % m_servers.size();  // 环形切换到下一个节点
            continue;
        }
  
        // 若服务器返回"键不存在"，返回空字符串
        if (reply.err() == common::ErrNoKey) {
            return "";
        }
  
        // 若服务器返回成功（OK），更新最近的领导者ID并返回value
        if (reply.err() == common::OK) {
            m_recentLeaderId = server;
            return reply.value();
        }
    }
    return "";  // 理论上不会走到这里（循环会一直重试直到成功）
}

// 核心实现：Put和Append的公共逻辑（向服务器发送写请求）
// 参数：key-键，value-值，op-操作类型（"Put"或"Append"）
void Clerk::PutAppend(std::string key, std::string value, std::string op) {
    m_requestId++;  // 递增请求ID（确保每个请求唯一）
    auto requestId = m_requestId;  // 保存当前请求ID（避免多线程下的冲突）
    auto server = m_recentLeaderId;  // 从最近已知的领导者节点开始尝试（优化效率）
  
    // 循环重试：直到找到真正的领导者并成功执行请求
    while (true) {
        // 构造PutAppend请求参数（符合protobuf定义的结构）
        raftKVRpcProctoc::PutAppendArgs args;
        args.set_key(key);       // 设置要操作的key
        args.set_value(value);   // 设置要操作的value
        args.set_op(op);         // 设置操作类型（"Put"或"Append"）
        args.set_clientid(m_clientId);  // 客户端唯一标识
        args.set_requestid(requestId);  // 本次请求唯一标识（与clientid组合确保全局唯一）
  
        raftKVRpcProctoc::PutAppendReply reply;  // 用于接收服务器响应
        // 调用RPC工具向目标节点发送请求
        bool ok = m_servers[server]->PutAppend(&args, &reply);
  
        // 若RPC调用失败（如网络问题）或服务器返回"错误的领导者"（当前节点不是领导者）
        if (!ok || reply.err() == common::ErrWrongLeader) {
            // 调试打印：输出重试原因和切换的节点
            common::DPrintf("【Clerk::PutAppend】原以为的leader：{%d}请求失败，向新leader{%d}重试 ，操作：{%s}", 
                        server, (server + 1) % m_servers.size(), op.c_str());
            if (!ok) {
                common::DPrintf("重试原因：rpc失败");
            }
            if (reply.err() == common::ErrWrongLeader) {
                common::DPrintf("重试原因：非leader");
            }
            // 切换到下一个节点（环形遍历所有节点）
            server = (server + 1) % m_servers.size();
            continue;  // 继续重试
        }
  
        // 若服务器返回成功（OK），更新最近的领导者ID并退出循环
        if (reply.err() == common::OK) {
                m_recentLeaderId = server;
                return;
        }
    }
}